package com.qqt.csr.file.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.URLUtil;
import com.google.common.collect.Lists;
import com.obs.services.ObsClient;
import com.obs.services.model.*;
import com.qqt.csr.common.enums.FileGroupEnum;
import com.qqt.csr.common.enums.FileTypeEnum;
import com.qqt.csr.common.exception.StatusCode;
import com.qqt.csr.common.utils.ServiceAssert;
import com.qqt.csr.file.config.HuaWeiObsProperties;
import com.qqt.csr.file.dto.MultipartUploadListingDTO;
import com.qqt.csr.file.dto.MultipartUploadTaskDTO;
import com.qqt.csr.file.service.IFileFolderService;
import com.qqt.csr.file.service.IMultipartFileService;
import com.qqt.csr.file.vo.req.InitMultiUploadReq;
import com.qqt.csr.file.vo.req.UploadPartReq;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.CacheControl;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MultipartFileHWOBSServiceImpl extends AbstractHWOBSService implements IMultipartFileService {
    @Autowired
    private IFileFolderService fileFolderService;

    @Override
    public String initiateMultipartUpload(String bucketName, InitMultiUploadReq req) {
        String fileName = FileUtil.getName(req.getFileName());
        String fileFolder = fileFolderService.getFileFolder(bucketName, req.getFileTypeEnum());
        String objectKey = fileFolder + fileName;

        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest();
        request.setBucketName(bucketName);
        request.setObjectKey(objectKey);
        // 默认对象公共读
        request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);

        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(req.getFileTypeEnum().getContentType());
        objectMetadata.setContentMd5(req.getMd5());
        if(FileGroupEnum.IMG.equals(req.getFileTypeEnum().getGroup())){
            objectMetadata.setContentDisposition(String.format("inline;attachment;filename=\"%s\"", fileName));
        }else{
            objectMetadata.setContentDisposition(String.format("attachment;filename=\"%s\"", fileName));
        }
        objectMetadata.setCacheControl(CacheControl.maxAge(1, TimeUnit.DAYS).mustRevalidate().getHeaderValue());
        objectMetadata.setObjectStorageClass(StorageClassEnum.STANDARD);
        request.setMetadata(objectMetadata);

        InitiateMultipartUploadResult result = obsClient.initiateMultipartUpload(request);
        ServiceAssert.isTrue(isRequestSuccess(result), StatusCode.Common.THIRD_API_ERROR.getCode(), "初始化分段上传任务失败");
        return result.getUploadId();
    }

    @Override
    public void uploadPart(String bucketName, UploadPartReq req, InputStream file) {
        String fileName = FileUtil.getName(req.getFileName());
        String fileFolder = fileFolderService.getFileFolder(bucketName, fileName);
        String objectKey = fileFolder + fileName;
        UploadPartRequest request = new UploadPartRequest();
        request.setBucketName(bucketName);
        request.setObjectKey(objectKey);
        request.setUploadId(req.getUploadId());
        request.setPartNumber(req.getChunkIndex());
        request.setInput(file);
        request.setAttachMd5(true);

        UploadPartResult uploadPartResult = obsClient.uploadPart(request);
        ServiceAssert.isTrue(isRequestSuccess(uploadPartResult), StatusCode.Common.THIRD_API_ERROR.getCode(), "分段上传失败");
    }

    @Override
    public String completeMultipartUpload(String bucketName, String objectName, String uploadId) {
        String fileName = FileUtil.getName(objectName);
        String fileFolder = fileFolderService.getFileFolder(bucketName, fileName);
        String objectKey = fileFolder + fileName;
        ListPartsRequest request = new ListPartsRequest(bucketName, objectKey);
        request.setUploadId(uploadId);
        ListPartsResult result = obsClient.listParts(request);
        ServiceAssert.notNull(result, StatusCode.Common.THIRD_API_ERROR.getCode(), "合并分段上传失败");

        List<PartEtag> partEtagList = Optional.ofNullable(result.getMultipartList()).orElseGet(Lists::newArrayList)
                .stream().map(part -> new PartEtag(part.getEtag(), part.getPartNumber())).collect(Collectors.toList());
        log.info("获取已上传的分段，bucketName:{},objectKey:{},statusCode:{},uploadId:{},size:{}", bucketName, objectKey, result.getStatusCode(), uploadId, partEtagList.size());
        ServiceAssert.isTrue(CollectionUtils.isNotEmpty(partEtagList), StatusCode.Common.THIRD_API_ERROR.getCode(), "无上传分段需要被合并");

        CompleteMultipartUploadRequest cmuReq = new CompleteMultipartUploadRequest(bucketName, objectKey, uploadId, partEtagList);
        CompleteMultipartUploadResult cmuResult = obsClient.completeMultipartUpload(cmuReq);
        ServiceAssert.notNull(cmuResult, StatusCode.Common.THIRD_API_ERROR.getCode(), "合并分段上传失败");
        log.info("合并分段上传结果，bucketName:{},objectKey:{},statusCode:{},uploadId:{},objectUrl:{}", cmuResult.getBucketName(), cmuResult.getObjectKey(), cmuResult.getStatusCode(), uploadId, cmuResult.getObjectUrl());
        ServiceAssert.isTrue(isRequestSuccess(cmuResult), StatusCode.Common.THIRD_API_ERROR.getCode(), "合并分段上传失败");

        return domainUrl + URLUtil.getPath(cmuResult.getObjectUrl());
    }

    @Override
    public void abortMultipartUpload(String bucketName, String objectName, String uploadId) {
        String fileName = FileUtil.getName(objectName);
        String fileFolder = fileFolderService.getFileFolder(bucketName, fileName);
        String objectKey = fileFolder + fileName;
        AbortMultipartUploadRequest request = new AbortMultipartUploadRequest(bucketName, objectKey, uploadId);
        HeaderResponse headerResponse = obsClient.abortMultipartUpload(request);
        ServiceAssert.isTrue(isRequestSuccess(headerResponse), StatusCode.Common.THIRD_API_ERROR.getCode(), "取消分段上传任务失败");
    }

    @Override
    public MultipartUploadListingDTO listMultipartUploads(String bucketName, String nextKeyMarker, String nextUploadIdMarker, int size) {
        ListMultipartUploadsRequest request = new ListMultipartUploadsRequest(bucketName);
        request.setMaxUploads(size);
        request.setKeyMarker(nextKeyMarker);
        request.setUploadIdMarker(nextUploadIdMarker);
        MultipartUploadListing result = obsClient.listMultipartUploads(request);

        MultipartUploadListingDTO listingDTO = new MultipartUploadListingDTO();
        listingDTO.setNextKeyMarker(result.getNextKeyMarker())
                .setNextUploadIdMarker(result.getNextUploadIdMarker())
                .setTruncated(result.isTruncated())
                .setUploadTaskDTOList(result.getMultipartTaskList().stream().map(upload -> MultipartUploadTaskDTO.builder()
                                .uploadId(upload.getUploadId())
                                .objectKey(upload.getObjectKey())
                                .initiatedDate(upload.getInitiatedDate())
                                .build())
                        .collect(Collectors.toList())
                );
        return listingDTO;
    }

}
